package com.qcrud.core.parsing;

import com.qcrud.core.SqlCommandType;
import com.qcrud.core.SqlManager;
import com.qcrud.core.SqlMeta;
import com.qcrud.exception.QcrudException;

import java.io.*;
import java.net.URL;
import java.util.*;

public class MdSqlParser {

    /**
     * MD SQL 文件解析
     *
     * @param fileName 文件名
     * @throws Exception
     */
    public static Map<String, SqlMeta> parse(String fileName) throws QcrudException, IOException {
        List<File> mdFiles = getMdFiles(fileName);
        if (mdFiles.isEmpty()) {
            throw new QcrudException("md file not found in filename: " + fileName);
        }
        for (File mdFile : mdFiles) {
            parse(new FileReader(mdFile), mdFile.getName());
        }
        return SqlManager.getSqlMetaMap();
    }

    /**
     * MD SQL 文件解析
     *
     * @param reader   {@link Reader}
     * @param fileName 文件名
     * @throws Exception
     */
    public static void parse(Reader reader, String fileName) throws QcrudException, IOException {
        LineNumberReader lineReader = new LineNumberReader(reader);
        int i = 0;
        int j = 0;
        String namespace = null;
        String methodName = null;
        String readLine;
        boolean findSql = false;
        StringBuilder sql = new StringBuilder();
        while ((readLine = lineReader.readLine()) != null) {
            ++i;
            int l = readLine.length();
            if (l > 2) {
                if (readLine.charAt(0) == '#') {
                    if (readLine.charAt(1) == '#') {
                        // 方法名称
                        methodName = readLine.substring(2).trim();
                        j = i;
                    } else {
                        // 命名空间
                        namespace = readLine.substring(1).trim();
                    }
                    continue;
                }

                // 存在方法名继续找到 sql 代码块
                if (null != methodName) {
                    if (findSql) {
                        // sql 代码块结束位置
                        if (Objects.equals(readLine, "```")) {
                            if ("".equals(sql.toString().trim())) {
                                throw new QcrudException("The methodName [ " + methodName + " ] sql code did not end correctly, line: " + j + " fileName: " + fileName);
                            }
                            // 存储命名空间及方法名，释放当前解析方法名及sql内容
                            String key = namespace + "." + methodName;
                            if (null != SqlManager.get(key)) {
                                throw new QcrudException("Namespace [ " + namespace + " ], methodName [ " + methodName + " ] already exists, line: " + j + " fileName: " + fileName);
                            }
                            // 方法名前缀匹配SQL命令类型
                            SqlManager.put(key, new SqlMeta(SqlCommandType.of(methodName), sql.toString().trim()));
                            methodName = null;
                            findSql = false;
                            sql.setLength(0);
                        } else {
                            // 执行 sql 读取
                            sql.append(readLine.trim()).append("\n");
                        }
                    } else {
                        // sql 代码块起始位置
                        findSql = Objects.equals(readLine, "```sql");
                    }
                }
            }

        }
    }

    public static List<File> getMdFiles(String filename) {
        URL url = MdSqlParser.class.getResource(filename);
        if (null == url) {
            return Collections.emptyList();
        }
        List<File> fileList = new ArrayList<>();
        File file = new File(url.getPath());
        if (file.isFile()) {
            return Collections.singletonList(file);
        }
        File[] files = file.listFiles();
        if (files == null || files.length == 0) {
            return Collections.emptyList();
        }
        for (File f : files) {
            if (f.isDirectory()) {
                List<File> childFiles = getDirectoryFiles(f.getPath());
                if (!childFiles.isEmpty()) {
                    fileList.addAll(childFiles);
                }
            } else if (isMd(f)) {
                fileList.add(f);
            }
        }
        return fileList;
    }

    protected static boolean isMd(File file) {
        return null != file && file.getName().endsWith(".md");
    }

    public static List<File> getDirectoryFiles(String path) {
        List<File> fileList = new ArrayList<>();
        File file = new File(path);
        File[] files = file.listFiles();
        if (files == null || files.length == 0) {
            return Collections.emptyList();
        }
        for (File f : files) {
            if (f.isDirectory()) {
                List<File> childFiles = getDirectoryFiles(path);
                if (!childFiles.isEmpty()) {
                    fileList.addAll(childFiles);
                }
            } else if (isMd(f)) {
                fileList.add(f);
            }
        }
        return fileList;
    }
}
